package gspringboot

import (
	"fmt"
	"gitee.com/hongzhaomin/gspring/annotation"
	"gitee.com/hongzhaomin/gspring/aware"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/hzm-common-go/assert"
	"gitee.com/hongzhaomin/xormplus"
	_ "github.com/go-sql-driver/mysql" // 需要导入mysql驱动
	"strconv"
	"strings"
	"xorm.io/xorm"
)

var _ aware.BeanFactoryAware = (*DataSourceRegister)(nil)
var _ annotation.Initialize = (*DataSourceRegister)(nil)

type DataSourceRegister struct {
	aware.Aware
	annotation.Initialize

	logger      iface.GspringLogger
	beanFactory iface.BeanFactory
	environment ConfigurationEnvironment
}

func (my *DataSourceRegister) SetBeanFactory(factory iface.BeanFactory) {
	my.beanFactory = factory
	my.logger = factory.GetBeanByName(LoggerBeanName).(iface.GspringLogger)
	my.environment = factory.GetBeanByName(EnvironmentBeanName).(ConfigurationEnvironment)
}

func (my *DataSourceRegister) Init() {
	logger := my.logger
	environment := my.environment
	// 存在代表开启了xormplus
	if ok := environment.ContainsKey(dataSourceMysqlPropName); !ok {
		return
	}
	// 打印banner
	new(xormplusBanner).PrintBanner(environment, logger)
	// 主数据源
	my.createPrimaryEngine()
	// 多数据源
	my.createMultipleEngine()
	// 设置日志
	xormplus.SetLogger(logger)
}

func (my *DataSourceRegister) register(beanName string, engine *xorm.Engine) {
	if registry, ok := my.beanFactory.(iface.ListableBeanFactory); ok {
		registry.RegisterSingleton(beanName, engine)
	}
	xormplus.RegisDataSources(beanName, engine)
}

func (my *DataSourceRegister) createPrimaryEngine() {
	logger := my.logger
	environment := my.environment
	logger.Info("Starting create mysql datasource")
	host := environment.GetStringReplacePlaceholders(dataSourceMysqlHostPropName)
	assert.NotBlank(host, fmt.Sprintf("property [%s] is blank", dataSourceMysqlHostPropName))
	port := environment.GetStringReplacePlaceholders(dataSourceMysqlPortPropName)
	assert.NotBlank(port, fmt.Sprintf("property [%s] is blank", dataSourceMysqlPortPropName))
	dbname := environment.GetStringReplacePlaceholders(dataSourceMysqlDbnamePropName)
	assert.NotBlank(dbname, fmt.Sprintf("property [%s] is blank", dataSourceMysqlDbnamePropName))
	username := environment.GetStringReplacePlaceholders(dataSourceMysqlUserNamePropName)
	assert.NotBlank(username, fmt.Sprintf("property [%s] is blank", dataSourceMysqlUserNamePropName))
	pwd := environment.GetStringReplacePlaceholders(dataSourceMysqlPasswordPropName)
	assert.NotBlank(pwd, fmt.Sprintf("property [%s] is blank", dataSourceMysqlPasswordPropName))
	dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8", username, pwd, host, port, dbname)
	engine, err := xorm.NewEngine("mysql", dataSourceName)
	if err != nil {
		panic(err)
	}

	// sql打印
	engine.ShowSQL(true)
	if environment.ContainsKey(xormSqlShowPropName) {
		isShow, _ := strconv.ParseBool(environment.GetStringReplacePlaceholders(xormSqlShowPropName))
		engine.ShowSQL(isShow)
	}

	// engine register
	my.register(dbname, engine)
	xormplus.SetPrimary(dbname, engine) // 设置主数据源
	logger.Info("Mysql datasource [%s] created success and registed", dbname)
}

func (my *DataSourceRegister) createMultipleEngine() {
	logger := my.logger
	environment := my.environment
	// 存在代表开启了多数据源
	if ok := environment.ContainsKey(dataSourceMysqlMultiplePropName); !ok {
		return
	}

	multipleDataSources, ok := environment.Get(dataSourceMysqlMultiplePropName).(map[string]any)
	if !ok {
		logger.Warn("Multiple datasource config error: %#v", multipleDataSources)
		return
	}
	logger.Info("The %d mysql multiple datasource need to load", len(multipleDataSources))
	for dataSourceId := range multipleDataSources {
		logger.Info("Starting create mysql multiple datasource")
		hostPropName := my.getMultipleConfigPropName(dataSourceMysqlHostPropName, dataSourceId)
		host := environment.GetStringReplacePlaceholders(hostPropName)
		assert.NotBlank(host, fmt.Sprintf("property [%s] is blank", hostPropName))

		portPropName := my.getMultipleConfigPropName(dataSourceMysqlPortPropName, dataSourceId)
		port := environment.GetStringReplacePlaceholders(portPropName)
		assert.NotBlank(port, fmt.Sprintf("property [%s] is blank", portPropName))

		dbnamePropName := my.getMultipleConfigPropName(dataSourceMysqlDbnamePropName, dataSourceId)
		dbname := environment.GetStringReplacePlaceholders(dbnamePropName)
		assert.NotBlank(dbname, fmt.Sprintf("property [%s] is blank", dbnamePropName))

		usernamePropName := my.getMultipleConfigPropName(dataSourceMysqlUserNamePropName, dataSourceId)
		username := environment.GetStringReplacePlaceholders(usernamePropName)
		assert.NotBlank(username, fmt.Sprintf("property [%s] is blank", usernamePropName))

		pwdPropName := my.getMultipleConfigPropName(dataSourceMysqlPasswordPropName, dataSourceId)
		pwd := environment.GetStringReplacePlaceholders(pwdPropName)
		assert.NotBlank(pwd, fmt.Sprintf("property [%s] is blank", pwdPropName))
		dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8", username, pwd, host, port, dbname)
		engine, err := xorm.NewEngine("mysql", dataSourceName)
		if err != nil {
			panic(err)
		}

		// sql打印
		engine.ShowSQL(true)
		if environment.ContainsKey(xormSqlShowPropName) {
			isShow, _ := strconv.ParseBool(environment.GetStringReplacePlaceholders(xormSqlShowPropName))
			engine.ShowSQL(isShow)
		}

		// engine register
		my.register(dbname, engine)
		logger.Info("Mysql multiple datasource [%s] created success and registed", dataSourceId)
	}
}

func (my *DataSourceRegister) getMultipleConfigPropName(key, dataSourceId string) string {
	split := strings.Split(key, ".")
	return strings.Join([]string{dataSourceMysqlMultiplePropName, dataSourceId, split[len(split)-1]}, ".")
}
